/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Array_h__
#define __dylab_Array_h__

//===========================================================================================
#include <dylab/utils/TokenParser.h>

//===========================================================================================
namespace dylab {

    /** 
     */    
    template < 
        typename _DataT,
        uint32_t _Size
    >
    class Array
    {
    public:
        typedef typename _DataT data_t;
        typedef typename Array<_DataT, _Size> ArrayT;

        static uint32_t getSize()
                { return _Size; }

        template <class _ArrayT>
        static _ArrayT parse(const String & _string, const String & _separators = " ,")
                {
                    // tokenize the string          
                    TokenParser tokens(_string, _separators);

                    // check the number of tokens - it has to match the size of the array
                    if (tokens.getTokens().getCount() != getSize())
                        throw EInvalidFormat(
							String::fmt("Number of tokens in string '%1%' does not match the size of the array (%2%)", 
								_string, getSize()));

                    // the tokens corresponds to the array elements
                    _ArrayT array;
                    uint32_t i = 0;
                    for (TokenParser::Tokens::Iterator token(tokens.getTokens()); token.isValid(); token++)            
                        array[i++] = token().parse<data_t>();            

                    // return the parsed array
                    return array;
                }

        static ArrayT parse(const String & _string, const String & _separators = " ,")
                { return ArrayT::parse<ArrayT>(_string, _separators); }

    protected:
        _DataT mArray[_Size];

    public:

        /**
        */
        Array()
                { }

        /**
        */
        Array(const ArrayT & _array)
                { copyFrom(_array.mArray); }

        /**
        */
        Array(_DataT (&_a)[_Size])
                { copyFrom(_a); }

        /**
        */
        virtual ~Array()
                { }

        /**
        */
        _DataT & operator[](uint32_t _index)
                {
                    DYLAB_ASSERT(_index < _Size);
                    return mArray[_index]; 
                }

        /**
        */
        const _DataT & operator[](uint32_t _index) const
                {
                    DYLAB_ASSERT(_index < _Size);
                    return mArray[_index]; 
                }

        /**
        */
        void copyFrom(const _DataT (&_a)[_Size])
            {
                for (uint32_t i = 0; i < _Size; i++)
                    mArray[i] = _a[i];
            }

        /**
        */
        void copyTo(_DataT (&_a)[_Size]) const
            {
                for (uint32_t i = 0; i < _Size; i++)
                    _a[i] = mArray[i];
            }

        /**
        */
        ArrayT & operator=(const ArrayT & _array)
                { copyFrom(_array.mArray); return *this; }

        /**
        */
        ArrayT & operator=(_DataT (&_a)[_Size])
                { copyFrom(_a); return *this; }
        //-----------------------------------------------------------------------------

    public:
        class Iterator
        {
        private:
            uint32_t mIndex;
            ArrayT * mArrayPtr;
        public:
            Iterator(ArrayT & _array, uint32_t _startingIndex = 0)
                : mIndex(_startingIndex), mArrayPtr(&_array)
                    { }
            bool isValid() const
                    { return mIndex < _Size; }
            uint32_t getIndex() const
                    { return mIndex; }
            _DataT & operator()()
                    { return (*mArrayPtr)[mIndex]; }
            const _DataT & operator()() const
                    { return (*mArrayPtr)[mIndex]; }
            Iterator & operator++(int)
                    { mIndex++; return *this; }
            Iterator & operator++()
                    { ++mIndex; return *this; }
        };
        //-----------------------------------------------------------------------------

    public:
        class ConstIterator
        {
        private:
            uint32_t mIndex;
            const ArrayT * mArrayPtr;
        public:
            ConstIterator(const ArrayT & _array, uint32_t _startingIndex = 0)
                : mIndex(_startingIndex), mArrayPtr(&_array)
                    { }
            bool isValid() const
                    { return mIndex < _Size; }
            uint32_t getIndex() const
                    { return mIndex; }
            const _DataT & operator()() const
                    { return (*mArrayPtr)[mIndex]; }
            Iterator & operator++(int)
                    { mIndex++; return *this; }
            Iterator & operator++()
                    { ++mIndex; return *this; }
        };
    };

    //===========================================================================================
    // array of 32-bit signed integers
    template <
        uint32_t _Size
    >
    class ArrayI32
        : public Array<int32_t, _Size>
    {
    public:
        typedef ArrayI32<_Size> MyArrayT;
        static MyArrayT parse(const String & _string, const String & _separators = " ,")
                { return Array<int32_t, _Size>::parse<MyArrayT>(_string, _separators); }
    public:
        ArrayI32()
                { }
        ArrayI32(data_t (&_a)[_Size])
            : ArrayT(_a)
                { }
        ArrayI32(const ArrayI32 & _array)
            : ArrayT(_array)
                { }
    };

    //===========================================================================================
    // array of 32-bit unsigned integers
    template <
        uint32_t _Size
    >
    class ArrayU32
        : public Array<uint32_t, _Size>
    {
    public:
        typedef ArrayU32<_Size> MyArrayT;
        static MyArrayT parse(const String & _string, const String & _separators = " ,")
                { return Array<uint32_t, _Size>::parse<MyArrayT>(_string, _separators); }
    public:
        ArrayU32()
                { }
        ArrayU32(data_t (&_a)[_Size])
            : ArrayT(_a)
                { }
        ArrayU32(const ArrayU32 & _array)
            : ArrayT(_array)
                { }
    };

    //===========================================================================================
    // array of real numbers
    template <
        uint32_t _Size
    >
    class ArrayReal
        : public Array<real_t, _Size>
    {
    public:
        typedef ArrayReal<_Size> MyArrayT;
        static MyArrayT parse(const String & _string, const String & _separators = " ,")
                { return Array<real_t, _Size>::parse<MyArrayT>(_string, _separators); }
    public:
        ArrayReal()
                { }
        ArrayReal(data_t (&_a)[_Size])
            : ArrayT(_a)
                { }
        ArrayReal(const ArrayReal & _array)
            : ArrayT(_array)
                { }
    };
}
//===========================================================================================
#endif // __dylab_Array_h__
